#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <fcntl.h>
#include <signal.h>
//要以文件IO的思想去学习这个IPC通信

/*
    实现两个无血缘关系进程的通信
*/
typedef struct shm_mem_info{
    pid_t read_pid;
	pid_t write_pid;
    char buf[1024];
}mem_info_t;

void sighandler(int signum)
{
    //这个打印不可加，因为父子进程都可能会调用该函数
    //printf("process pid = [%d], recv [%s]\n", getpid(), signum);
    return ;
}

int main(int argc, char* argv[]){
    int ret = 0;
	int len = 0;
    char buf[1024] = {0};
    //1、这个key用于两个非血缘关系的进程实现通信，如果只是父子进程通信，可以不设置这个key,用IPC_PRIVATE代替
    key_t key = ftok("./tmp_mem.txt", 'a');
    if(key < 0){
        perror("ftok() error:");
        exit(-1);
    }
    printf("key = [%ld]\n", key);

    //2、在内核中创建一块共享内存
    int shmid = shmget(key, 1024, IPC_CREAT|0777);
    if(shmid < 0){
        perror("shmget() error:");
        exit(-1);
    }
    
    //3、即将内核中共享内存区域映射到用户区的进程地址空间中，节省了调用开销
    mem_info_t *mem_info = (mem_info_t *)shmat(shmid, NULL, 0);
    if(mem_info == NULL){
        perror("shmat() error:");
        exit(-1);
    }    
    system("ipcs -m");

	//注册信号处理函数，用于唤醒pause()
	signal(SIGUSR2, sighandler);

	printf("read process: mem_info->write_pid = [%d]\n", mem_info->write_pid);
	mem_info->read_pid = getpid();
	kill(mem_info->write_pid, SIGUSR1);

    //4、写共享内存
    while(1){
		pause(); //wait 父进程写数据到共享内存
		printf("read process:%s\n", mem_info->buf);
		kill(mem_info->write_pid, SIGUSR1);
		if(!strncmp(mem_info->buf, "exit", 4)){
			break;
		}
    }
    
    //5、将进程里的地址映射删除
    ret = shmdt(mem_info);
    if(ret < 0){
        perror("shmdt() error:");
        exit(-1);
    }

	//read 进程收到exit信息之后先退出，一个进程删除内存即可，write进程无需在做删除操作
    //6、删除共享内存
    ret = shmctl(shmid, IPC_RMID, NULL);
    if(ret < 0){
        perror("shmctl() error:");
        exit(-1);
    }
    //system("ipcs -m");
    return 0;    
}
